#install.packages("knitr")
#install.packages("grid")
#install.packages("plotly")
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")
#install.packages("RColorBrewer")
#install.packages("rgdal")
#install.packages("jsonlite")
#install.packages("readr")
#install.packages("readr")
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
#library(normalr)
library(ggcorrplot)
library(leaflet)
library(plotly)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
library(RColorBrewer)
library(readr)
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
dbname = "coronavirus",
host = "localhost"
)
`src_mysql()` is deprecated as of dplyr 1.0.0.
Please use `tbl()` directly with a database connection
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
my_db
src: mysql 10.4.17-MariaDB [root@localhost:/coronavirus]
tbls: avg_world_temp_2020, covid19_confirmed, covid19_deaths, covid19_recovered, covid19_thailand,
covidus, data_gender, data_lockdown, data_population, gdp_us, gdp19, healthranking, population,
pornhub, sars_2003_update, time_series_covid19_confirmed_global, time_series_covid19_deaths_global,
time_series_covid19_recovered_global, us_homeless
##import data
df_conf <- tbl(my_db, sql("select * from time_series_covid19_confirmed_global "))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from time_series_covid19_deaths_global "))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from time_series_covid19_recovered_global "))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
## remove some columns
data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
## convert from wide to long format
data %<>% gather(key=date, value=count, -country)
## convert from character to date
data %<>% mutate(date = date %>% mdy())
## aggregate by country
data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
data
#rate
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1))
View(data)
## convert from wide to long format
data.long <- data %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.long)
##Number of case World
world <- filter(data.long,country == 'World')
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
ggplot(aes(x=date, y=count)) +
geom_line(aes(color=type)) +
labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=14),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=14),
axis.text=element_text(size=14),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)
plot2
## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)
View(data.world)
## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)
## convert from wide to long format, for drawing area plots
rates.long <- data %>%
select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
rate.upper='Upper bound'))
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
select(country, date,confirmed, new.confirmed, current.confirmed,
recovered, deaths, new.deaths, death.rate=rate.lower) %>%
mutate(ranking = dense_rank(desc(confirmed)))
#View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
current.confirmed=sum(current.confirmed),
recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
mutate(death.rate=(100 * deaths/confirmed) %>% round(1))
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths, current.confirmed,recovered)) %>%
mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
df_pop
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE)
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths,
current.confirmed,recovered,recover.rate,population)) %>%
mutate(confirm.rate=(100 *confirmed/population) %>% round(1))
data.latest
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
confirmed='Total Confirmed',
deaths='Total Deaths',
death.rate='Death Rate (%)',
new.confirmed='New Confirmed (compared with one day before)',
new.deaths='New Deaths (compared with one day before)',
current.confirmed='Current Confirmed',
recover.rate = 'Recover Rate(%)',
confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
| type=='Total Deaths'
| type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Confirmed Rate(%)'
#| type=='New Confirmed (compared with one day before)'
#| type=='New Deaths (compared with one day before)'
| type=='Death Rate (%)'
| type=='Recover Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=2, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=11),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')
data.two.dem$facet <- factor(data.two.dem$type, levels = c('Confirmed Rate(%)', 'Recover Rate(%)','Death Rate (%)'))
data.two.dem %>%
ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=2, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=11),
axis.text=element_text(size=9),
axis.text.x=element_text(size=6,angle=45, hjust=1)) +
facet_wrap(~facet, ncol=1, scales='free_y')
##GDP
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
df_healt
#Top20Pornhub
df_pornhub <- tbl(my_db, sql("select * from Pornhub"))
df_pornhub <- as.data.frame(df_pornhub)
df_pornhub
#temp
df_temp <- tbl(my_db, sql("select * from Avg_World_Temp_2020"))
df_temp <- as.data.frame(df_temp)
df_city <- select(df_temp,c("Country","City")) %>%
rename(country=Country) %>%
rename(city=City)
numofcity <- aggregate(city ~ country, data = df_city, length)
df_temp <- select(df_temp,c("Country","Apr","May","Jun","Jul","Aug")) %>%
rename(country=Country)
df_temp <- data.frame(country=df_temp[,1],avg=rowMeans(df_temp[,-1]))
df_temp <- df_temp %<>% group_by(country) %>% summarise(avg_temp = mean(avg,na.rm = TRUE))
df_temp <- df_temp %>% mutate(country=ifelse(country=="United States","US", country ) )
df_temp$avg_temp <- df_temp$avg_temp %>%
sprintf(df_temp$avg_temp, fmt = '%#.1f') %>%
as.numeric(df_temp$avg_temp)
df_temp
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World')
data.top <- head(data.top,20)
#View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
#View(data.gdp)
#merge
mergcountry = function(data1,data2){
data <- merge(x = data1, y = data2, by = "country", all.x = TRUE)
return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>%
select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>%
rename(GDP="GDP (millions of US dollars)")
data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)
data.top.world <- merge(x = data.top.world, y = df_pornhub, by = "country", all.x = TRUE) %>%
rename(Pornhub = "PornhubIndex(%)")
data.top.world <- mergcountry(data.top.world, df_temp)
index <- is.na(data.top.world)
data.top.world[index] <- 0
data.top.world
#View(data.top.world)
normalize = function(data){
#return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
z <- scale(data);
tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:12],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
#View(norm_data)
norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","Pornhub","GDP","avg_temp")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type,
level = c("GDP","avg_temp","healthcare","recover.rate","death.rate","confirm.rate","Pornhub"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) +
geom_tile() +
scale_fill_gradient(low = "pink", high = "blue") +
xlab("") +
ylab("") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
#rank GDP
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(desc(GDP)))
data.top.hight
k <- 15
top.gdp <- data.top.hight %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
top.gdp <- head(top.gdp,21)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
View(low.gdp.long)
low.gdp <- head(low.gdp.long,23)
low.gdp
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare,avg_temp,Pornhub))
head(corr_data)
cor(corr_data)
ggcorrplot(cor(corr_data),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)
df <- data.long %>% filter(country %in% top.countries) %<>%
mutate(country=country %>% factor(levels=c(top.countries)))
df %>% filter(country != 'World' & type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count, fill=type)) +
geom_area(alpha=0.5) +
# xlab('') + ylab('') +
labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries - ',
max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=12),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.4, 'cm'),
legend.text=element_text(size=12),
strip.text.x=element_text(size=12),
axis.text=element_text(size=12),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~country, ncol=4, scales='free_y')
p <- df %>% filter(country != 'World') %>%
ggplot(aes(x=date, y=count, color=type)) +
geom_line() +
labs(title=paste0('Numbers of COVID-19 Cases in Top 20 Countries (log scale) - ',
max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=10),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.4, 'cm'),
legend.text=element_text(size=10),
strip.text.x=element_text(size=10),
axis.text=element_text(size=10),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
p + facet_wrap(~country, ncol=4, scales='free_y')
data.world %<>% arrange(desc(date)) %>%
select(c(date, confirmed, deaths, recovered, current.confirmed,new.confirmed, new.deaths, new.recovered, rate.lower, rate.upper, rate.daily))
data.world %>%
mutate(rate.upper = rate.upper %>% format(nsmall=1) %>% paste0('\\%'),
rate.lower = rate.lower %>% format(nsmall=1) %>% paste0('\\%'),
rate.daily = rate.daily %>% format(nsmall=1) %>% paste0('\\%'))
#sars_2003
df_sars <- tbl(my_db, sql("select * from sars_2003_update"))
df_sars <- as.data.frame(df_sars)
df_sars
## convert from character to date
#datesSar <- as.Date(df_sars$Date,format = "%m/%d/%y")
df_sars %<>% mutate(Date = as.Date(df_sars$Date,format = "%m/%d/%y"))
df_sars
## convert from wide to long format
dataSar.long <- df_sars %>%
select(c(Date, country, Cumulative_number , Number_deaths, Number_recovered)) %>%
gather(key=type, value=count, -c(country, Date))
## set factor levels to show them in a desirable order
dataSar.long %<>% mutate(type=recode_factor(type, Cumulative_number ='Cumulative Number',
Number_deaths ='Number of deaths',
Number_recovered ='Number of recovered'))
View(dataSar.long)
g <-
ggplot(dataSar.long,aes(Date,count,color = type)) +
geom_line()+
geom_point()+
xlab("")+
ylab("")
g
#Covid_Thailand
df_thai <- tbl(my_db, sql("select * from covid_Thailand"))
df_thai <- as.data.frame(df_thai)
View(df_thai)
#clean Covid_Thailand
dates.th <- df_thai[,2]%>% mdy()
range(dates.th)
min.date.th <- min(dates.th)
max.date.th <- max(dates.th)
min.date.txt.th <- min.date.th %>% format('%d %b %Y')
max.date.txt.th <- max.date.th %>% format('%d %b %Y')
df_thai$announce_date <- mdy(df_thai$announce_date)
df_thai$notification_date <- mdy(df_thai$notification_date)
df_thai
df_thai <- df_thai %>% select(!No.) %>% select(!notification_date) %>%
group_by(announce_date)
df_thai
# Total confirmed cases in Thailand
data.thai.count <- df_thai %>%
select(announce_date) %>%
summarise(comfirmed = n()) %>% as.data.frame()
data.thai.count$cumulative_confirmed <- cumsum(data.thai.count[, 2])
data.thai.count
## Thai Confirmed Cases (Jan 2020 - Jan 2021
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
geom_point() + geom_smooth() +
xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021)') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
geom_point() + geom_smooth() +
xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021)') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)
## Thai Confirmed Cases (Jan 2020 - Jan 2021) log scale
plot1 <- ggplot(data.thai.count, aes(x=announce_date, y=cumulative_confirmed)) +
geom_point() + geom_smooth() +
xlab(" ") + ylab("Count") + labs(title='Thai Cumulative Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
plot2 <- ggplot(data.thai.count, aes(x=announce_date, y=comfirmed)) +
geom_point() + geom_smooth() +
xlab(" ") + ylab("Count")+ labs(title='Thai Confirmed Cases (Jan 2020 - Jan 2021 log scale)') +
theme(axis.text.x=element_text(angle=45, hjust=1))+scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=1)
# Confirmed cases divided by sex (gender)
data.thai.gender <- df_thai %>%
group_by(sex) %>%
summarise(count = n()) %>%
mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
filter(percent>1)%>%
#mutate(pos = cumsum(percent) - 0.5*percent) %>%
arrange(desc(percent))
data.thai.gender
data.thai.gender$sex <- factor(data.thai.gender$sex, levels = as.character(data.thai.gender$sex))
data.thai.gender$sex
g.th.gender <- data.thai.gender %>%
ggplot(aes(x = "", y = percent, fill = sex)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y") +
theme_void() +
labs(title='Gender of Thai Confirmed Cases (Jan 2020 - Jan 2021)')+
geom_text(aes(label = paste0(percent, "%")), color = "white", size = 5, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.th.gender
# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
group_by(risk) %>%
summarise(count = n()) %>%
arrange(desc(count))
data.thai.risk
# Confirmed cases divided by risk
data.thai.risk <- df_thai %>%
group_by(risk) %>%
summarise(count = n()) %>%
mutate(percent = (count / sum(count) * 100) %>% round(2)) %>%
filter(percent>3.5) %>%
arrange(desc(percent))
data.thai.risk
data.thai.risk$risk[data.thai.risk$risk == "C"] <- "Close contact with the patient"
data.thai.risk$risk[data.thai.risk$risk == "F"] <- "State Quarantine"
data.thai.risk$risk[data.thai.risk$risk == "O"] <- "AOQ/ALQ/HQ/AHQ/OQ"
data.thai.risk$risk[data.thai.risk$risk == "G"] <- "Go to a crowded place"
data.thai.risk$risk[data.thai.risk$risk == "B"] <- "Thai people from abroad"
data.thai.risk$risk[data.thai.risk$risk == "D"] <- "Career at risk"
data.thai.risk$risk[data.thai.risk$risk == "H"] <- "Cabaret"
data.thai.risk
data.thai.risk$risk <- factor(data.thai.risk$risk, levels = as.character(data.thai.risk$risk))
data.thai.risk$risk
g.th.risk <- data.thai.risk %>%
ggplot(aes(x = "", y = percent, fill = risk)) +
geom_bar(stat = "identity", width = 0.5) +
coord_polar("y") +
theme_void() +
labs(title='Risk of Thai Confirmed Cases(Jan 2020 - Jan 2021)')+
geom_text(aes(label = paste0(percent, "%")), color = "Black", size = 3, position = position_stack(vjust = 0.5)) +
guides(fill = guide_legend(reverse = TRUE))
g.th.risk
# Confirmed cases divided by age
data.thai.age <- df_thai %>%
group_by(age,sex) %>%
filter(sex != "")%>%
summarise(count = n()) %>%
arrange(desc(count))
data.thai.age
ggplot(data.thai.age,aes(x=age,y=count,fill=sex))+geom_bar(stat = "identity")+
labs(title='Age of Thai Confirmed Cases')+guides(fill=guide_legend(reverse = T))
# Confirmed cases divided by nationality
data.thai.nationality <- df_thai %>%
group_by(nationality) %>%
summarise(count = n()) %>%
filter(count > 11)%>%
arrange(desc(count))
data.thai.nationality$nationality[data.thai.nationality$nationality == "????????"] <- "Unknown"
data.thai.nationality$nationality[data.thai.nationality$nationality == ""] <- "Unknown"
data.thai.nationality
ggplot(data.thai.nationality,aes(x=nationality,y=count))+geom_bar(stat = "identity")+
labs(title='Nationality of Thai Confirmed Cases')+coord_flip()
#Covid_US
df_us <- tbl(my_db, sql("select * from covidUs"))
df_us <- as.data.frame(df_us)
df_us
#clean Covid_US
dates.us <- df_us[,2]%>% mdy()
range(dates.us)
min.date.us <- min(dates.us)
max.date.us <- max(dates.us)
min.date.txt.us <- min.date.us %>% format('%d %b %Y')
max.date.txt.us <- max.date.us %>% format('%d %b %Y')
df_us$date <- mdy(df_us$date)
df_us
df_us <- df_us %>% select(!MyUnknownColumn) %>% select(!fips) %>%
group_by(date)
df_us
## convert from wide to long format
data.long.us <- df_us %>%
select(c(date, state, cases, deaths)) %>%
gather(key=type, value=count, -c(date, state))
## set factor levels to show them in a desirable order
data.long.us %<>% mutate(type=recode_factor(type, cases='Confirmed',
deaths='Deaths'))
View(data.long.us)
ggplot(data.long.us,aes(x=date,y=count))+
geom_line(aes(color=type))+ labs(title='US Confirmed Cases (Jan 2020 - Dec 2020)')
#gender in us
df_gender_us <- tbl(my_db, sql("select * from data_gender"))
df_gender_us <- as.data.frame(df_gender_us)
df_gender_us <- select(df_gender_us,c("State","Male","Female"))
df_gender_us <- rename(df_gender_us,"state"="State")
df_gender_us
#population in us
df_pop_us <- tbl(my_db, sql("select * from data_population"))
df_pop_us <- as.data.frame(df_pop_us)
df_pop_us <- select(df_pop_us,c("State","Population"))
df_pop_us <- rename(df_pop_us,"state"="State")
df_pop_us
#lockdown in us
df_lockdown_us <- tbl(my_db, sql("select * from data_lockdown"))
df_lockdown_us <- as.data.frame(df_lockdown_us)
df_lockdown_us <- select(df_lockdown_us,c("State","Day lockdown"))
df_lockdown_us <- rename(df_lockdown_us,"state"="State")
df_lockdown_us
#GDP in us
df_gdp_us <- tbl(my_db, sql("select * from gdp_us"))
df_gdp_us <- as.data.frame(df_gdp_us)
df_gdp_us <- select(df_gdp_us,c("State","GDPs"))
df_gdp_us <- rename(df_gdp_us,"state"="State")
df_gdp_us
#homeless in us
df_homeless_us <- tbl(my_db, sql("select * from us_homeless"))
df_homeless_us <- as.data.frame(df_homeless_us)
df_homeless_us <- select(df_homeless_us,c("State","Homeless"))
df_homeless_us <- rename(df_homeless_us,"state"="State")
df_homeless_us
#merge
mergcountry = function(data1,data2){
data <- merge(x = data1, y = data2, by = "state", all.x = TRUE)
return(data)
}
df_Allus <- merge(x = df_us, y = df_gender_us, by = "state", all.x = TRUE)
df_Allus <- merge(x = df_Allus, y = df_pop_us, by = "state", all.x = TRUE)
df_Allus <- merge(x = df_Allus, y = df_lockdown_us, by = "state", all.x = TRUE)
df_Allus <- merge(x = df_Allus, y = df_gdp_us, by = "state", all.x = TRUE)
df_Allus <- merge(x = df_Allus, y = df_homeless_us, by = "state", all.x = TRUE)
View(df_Allus)
index <- is.na(df_Allus)
df_Allus[index] <- 0
normalize = function(data){
#return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
z <- scale(data);
tanh(z/2)
}
Allus = as.data.frame(apply(df_Allus[,3:10],2,normalize))
corr_dataUS <- Allus
View(Allus)
Allus$state <- c(df_Allus$state)
View(Allus)
Allus_plot <- select(Allus,"state","cases","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless")
Allus_plot %<>% gather(key=type, value=count, -c(state))
level_order <- factor(Allus_plot$type,
level = c("cases","deaths","Male","Female","Population","Day lockdown","GDPs","Homeless"))
ggplot(data = Allus_plot, aes(x=state, y=level_order, fill=count)) +
geom_tile() +
scale_fill_gradient(low = "pink", high = "blue") +
xlab("") +
ylab("") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)
corr_dataUS <- rename(corr_dataUS,"Daylockdown"="Day lockdown")
#correlation
corr_dataUS %<>% select(c(cases,deaths,Male,Female,Population,Daylockdown,GDPs,Homeless))
head(corr_dataUS)
cor(corr_dataUS)
ggcorrplot(cor(corr_dataUS),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KI2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHByZXAiKQ0KI2luc3RhbGwucGFja2FnZXMoIm5vcm1hbHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnY29ycnBsb3QiKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygiUkNvbG9yQnJld2VyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZ2RhbCIpDQojaW5zdGFsbC5wYWNrYWdlcygianNvbmxpdGUiKQ0KI2luc3RhbGwucGFja2FnZXMoInJlYWRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpDQpgYGANCg0KYGBge3J9DQoNCg0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoa25pdHIpDQojbGlicmFyeShub3JtYWxyKQ0KbGlicmFyeShnZ2NvcnJwbG90KQ0KDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShyZWFkcikNCiNsaWJyYXJ5KE1MUk1QQSkNCiM/P3NyY19teXNxbA0KbXlfZGIgPC0gc3JjX215c3FsKA0KICBkYm5hbWUgPSAiY29yb25hdmlydXMiLA0KICBob3N0ID0gImxvY2FsaG9zdCINCikNCm15X2RiDQoNCiMjaW1wb3J0IGRhdGENCmRmX2NvbmYgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdGltZV9zZXJpZXNfY292aWQxOV9jb25maXJtZWRfZ2xvYmFsICIpKQ0KZGZfY29uZiA8LSBhcy5kYXRhLmZyYW1lKGRmX2NvbmYpDQpkZl9jb25mDQpkZl9kZWF0aHMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdGltZV9zZXJpZXNfY292aWQxOV9kZWF0aHNfZ2xvYmFsICIpKQ0KZGZfZGVhdGhzIDwtIGFzLmRhdGEuZnJhbWUoZGZfZGVhdGhzKQ0KZGZfZGVhdGhzDQpkZl9yZWNvdmVyIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIHRpbWVfc2VyaWVzX2NvdmlkMTlfcmVjb3ZlcmVkX2dsb2JhbCAiKSkNCmRmX3JlY292ZXIgPC0gYXMuZGF0YS5mcmFtZShkZl9yZWNvdmVyKQ0KZGZfcmVjb3Zlcg0KYGBgDQpgYGB7cn0NCiMjY2hlY2sgdGhlIHRpbWUgZnJhbWUgb2YgdGhlIGRhdGENCm4uY29sIDwtIG5jb2woZGZfY29uZikNCmRhdGVzIDwtIG5hbWVzKGRmX2NvbmYpWzU6bi5jb2xdJT4lIG1keSgpDQpyYW5nZShkYXRlcykNCm1pbi5kYXRlIDwtIG1pbihkYXRlcykNCm1heC5kYXRlIDwtIG1heChkYXRlcykNCm1pbi5kYXRlLnR4dCA8LSBtaW4uZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQgPC0gbWF4LmRhdGUgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KYGBgDQpgYGB7cn0NCiNjbGVhbiBkYXRhDQpjbGVhbkRhdGEgPC0gZnVuY3Rpb24oZGF0YSkgew0KICAjIyByZW1vdmUgc29tZSBjb2x1bW5zDQogIGRhdGEgJTw+JSBzZWxlY3QoLWMoUHJvdmluY2UuU3RhdGUsIExhdCwgTG9uZykpICU+JSByZW5hbWUoY291bnRyeT1Db3VudHJ5LlJlZ2lvbikNCiAgIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCiAgZGF0YSAlPD4lIGdhdGhlcihrZXk9ZGF0ZSwgdmFsdWU9Y291bnQsIC1jb3VudHJ5KQ0KICAjIyBjb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIGRhdGUNCiAgZGF0YSAlPD4lIG11dGF0ZShkYXRlID0gZGF0ZSAlPiUgbWR5KCkpDQogICMjIGFnZ3JlZ2F0ZSBieSBjb3VudHJ5DQogIGRhdGEgJTw+JSBncm91cF9ieShjb3VudHJ5LCBkYXRlKSAlPiUgc3VtbWFyaXNlKGNvdW50PXN1bShjb3VudCwgbmEucm09VCkpICU+JSBhcy5kYXRhLmZyYW1lKCkNCiAgcmV0dXJuKGRhdGEpDQp9DQojIyBjbGVhbiB0aGUgdGhyZWUgZGF0YSBzZXRzDQpkYXRhLmNvbmZpcm1lZCA8LSBkZl9jb25mICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKGNvbmZpcm1lZD1jb3VudCkNCmRhdGEuZGVhdGhzIDwtIGRmX2RlYXRocyAlPiUgY2xlYW5EYXRhKCkgJT4lIHJlbmFtZShkZWF0aHM9Y291bnQpDQpkYXRhLnJlY292ZXJlZCA8LSBkZl9yZWNvdmVyICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKHJlY292ZXJlZD1jb3VudCkNCmRhdGEgPC0gZGF0YS5jb25maXJtZWQgJT4lIG1lcmdlKGRhdGEuZGVhdGhzLCBhbGw9VCkgJT4lIG1lcmdlKGRhdGEucmVjb3ZlcmVkLCBhbGw9VCkNCmRhdGENCiMjIGNvdW50cmllcy9yZWdpb25zIHdpdGggY29uZmlybWVkIGNhc2VzLCBleGNsLiBjcnVpc2Ugc2hpcHMNCmNvdW50cmllcyA8LSBkYXRhICU+JSBwdWxsKGNvdW50cnkpICU+JSBzZXRkaWZmKCdDcnVpc2UgU2hpcCcpDQpkYXRhIA0KYGBgDQoNCg0KYGBge3J9DQpkYXRhLndvcmxkIDwtIGRhdGEgJT4lIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnRyeT0nV29ybGQnLA0KICAgICAgICAgICAgY29uZmlybWVkID0gc3VtKGNvbmZpcm1lZCwgbmEucm09VCksDQogICAgICAgICAgICBkZWF0aHMgPSBzdW0oZGVhdGhzLCBuYS5ybT1UKSwNCiAgICAgICAgICAgIHJlY292ZXJlZCA9IHN1bShyZWNvdmVyZWQsIG5hLnJtPVQpKQ0KZGF0YSAlPD4lIHJiaW5kKGRhdGEud29ybGQpDQpkYXRhDQpkYXRhICU8PiUgbXV0YXRlKGN1cnJlbnQuY29uZmlybWVkID0gY29uZmlybWVkIC0gZGVhdGhzIC0gcmVjb3ZlcmVkKQ0KZGF0YQ0KDQpgYGANCmBgYHtyfQ0KI3JhdGUNCmRhdGEgJTw+JSBhcnJhbmdlKGNvdW50cnksIGRhdGUpDQpuIDwtIG5yb3coZGF0YSkNCmRheTEgPC0gbWluKGRhdGEkZGF0ZSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBjb25maXJtZWQgLSBsYWcoY29uZmlybWVkLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LmRlYXRocyA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCBkZWF0aHMgLSBsYWcoZGVhdGhzLCBuPTEpKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShkYXRlID09IGRheTEsIE5BLCByZWNvdmVyZWQgLSBsYWcocmVjb3ZlcmVkLCBuPTEpKSkNCmRhdGEgJTw+JSBtdXRhdGUobmV3LmNvbmZpcm1lZCA9IGlmZWxzZShuZXcuY29uZmlybWVkIDwgMCwgMCwgbmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UobmV3LmRlYXRocyA8IDAsIDAsIG5ldy5kZWF0aHMpLA0KICAgICAgICAgICAgICAgICBuZXcucmVjb3ZlcmVkID0gaWZlbHNlKG5ldy5yZWNvdmVyZWQgPCAwLCAwLCBuZXcucmVjb3ZlcmVkKSkNCiMjIGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgZGVhdGhzIGFuZCByZWNvdmVyZWQgY2FzZXMNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS51cHBlciA9ICgxMDAgKiBkZWF0aHMgLyAoZGVhdGhzICsgcmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpKQ0KIyMgbG93ZXIgYm91bmQ6IGRlYXRoIHJhdGUgYmFzZWQgb24gdG90YWwgY29uZmlybWVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUubG93ZXIgPSAoMTAwICogZGVhdGhzIC8gY29uZmlybWVkKSAlPiUgcm91bmQoMSkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgZGVhdGgvcmVjb3ZlcmVkIG9uIGV2ZXJ5IHNpbmdsZSBkYXkNCmRhdGEgJTw+JSBtdXRhdGUocmF0ZS5kYWlseSA9ICgxMDAgKiBuZXcuZGVhdGhzIC8gKG5ldy5kZWF0aHMgKyBuZXcucmVjb3ZlcmVkKSkgJT4lIHJvdW5kKDEpKQ0KVmlldyhkYXRhKQ0KYGBgDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0DQpkYXRhLmxvbmcgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwgcmVjb3ZlcmVkLCBkZWF0aHMpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgZGF0ZSkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGEubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwgY29uZmlybWVkPSdUb3RhbCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD0nQ3VycmVudCBDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyZWQ9J1JlY292ZXJlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nRGVhdGhzJykpDQpWaWV3KGRhdGEubG9uZykNCmBgYA0KYGBge3J9DQojI051bWJlciBvZiBjYXNlIFdvcmxkDQp3b3JsZCA8LSBmaWx0ZXIoZGF0YS5sb25nLGNvdW50cnkgPT0gJ1dvcmxkJykNCnBsb3QxIDwtIHdvcmxkICU+JSBmaWx0ZXIodHlwZSAhPSAnVG90YWwgQ29uZmlybWVkJykgJT4lDQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWNvdW50KSkgKw0KICBnZW9tX2FyZWEoYWVzKGZpbGw9dHlwZSksIGFscGhhPTAuNSkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDYXNlcyBXb3JsZHdpZGUgLSAnLCBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIHdvcmxkICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj10eXBlKSkgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDYXNlcyBXb3JsZHdpZGUgKGxvZyBzY2FsZSkgLSAnLCBtYXguZGF0ZS50eHQpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygncHVycGxlJywgJ3JlZCcsICdncmVlbicsICdibGFjaycpKSArDQogIHRoZW1lKGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKQ0KIyMgc2hvdyB0d28gcGxvdHMgc2lkZSBieSBzaWRlDQpncmlkLmFycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTIpDQpgYGANCg0KDQpgYGB7cn0NCnBsb3QyDQpgYGANCmBgYHtyfQ0KIyMgQ3VycmVudCBDb25maXJtZWQgQ2FzZXMNCmRhdGEud29ybGQgPC0gZGF0YSAlPiUgZmlsdGVyKGNvdW50cnk9PSdXb3JsZCcpDQpuIDwtIG5yb3coZGF0YS53b3JsZCkNCnBsb3QxIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PWN1cnJlbnQuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQ3VycmVudCBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuY29uZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nRGFpbHkgTmV3IENvbmZpcm1lZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KVmlldyhkYXRhLndvcmxkKQ0KYGBgDQpgYGB7cn0NCiMjIGEgc2NhdHRlciBwbG90IHdpdGggYSBzbW9vdGhlZCBsaW5lIGFuZCB2ZXJ0aWNhbCB4LWF4aXMgbGFiZWxzDQpwbG90MSA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1kZWF0aHMpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdBY2N1bXVsYXRpdmUgRGVhdGhzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9cmVjb3ZlcmVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIFJlY292ZXJlZCBDYXNlcycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QzIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5kZWF0aHMpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgRGVhdGhzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDQgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LnJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J05ldyBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IGZvdXIgcGxvdHMgdG9nZXRoZXIsIHdpdGggMiBwbG90cyBpbiBlYWNoIHJvdw0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgcGxvdDMsIHBsb3Q0LCBucm93PTIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGZvciBkcmF3aW5nIGFyZWEgcGxvdHMNCnJhdGVzLmxvbmcgPC0gZGF0YSAlPiUNCiAgc2VsZWN0KGMoY291bnRyeSwgZGF0ZSwgcmF0ZS51cHBlciwgcmF0ZS5sb3dlciwgcmF0ZS5kYWlseSkpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5LCBkYXRlKSkNCiMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpyYXRlcy5sb25nICU8PiUgbXV0YXRlKHR5cGU9cmVjb2RlX2ZhY3Rvcih0eXBlLCByYXRlLmRhaWx5PSdEYWlseScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGUudXBwZXI9J1VwcGVyIGJvdW5kJykpDQpgYGANCmBgYHtyfQ0KIyMgcmFua2luZyBieSBjb25maXJtZWQgY2FzZXMNCmRhdGEubGF0ZXN0LmFsbCA8LSBkYXRhICU+JSBmaWx0ZXIoZGF0ZSA9PSBtYXgoZGF0ZSkpICU+JQ0KICBzZWxlY3QoY291bnRyeSwgZGF0ZSxjb25maXJtZWQsIG5ldy5jb25maXJtZWQsIGN1cnJlbnQuY29uZmlybWVkLA0KICAgICAgICAgcmVjb3ZlcmVkLCBkZWF0aHMsIG5ldy5kZWF0aHMsIGRlYXRoLnJhdGU9cmF0ZS5sb3dlcikgJT4lDQogIG11dGF0ZShyYW5raW5nID0gZGVuc2VfcmFuayhkZXNjKGNvbmZpcm1lZCkpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QuYWxsKQ0KayA8LSAyMA0KIyMgdG9wIDIwIGNvdW50cmllczogMjEgaW5jbC4gJ1dvcmxkJw0KdG9wLmNvdW50cmllcyA8LSBkYXRhLmxhdGVzdC5hbGwgJT4lIGZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUNCiAgYXJyYW5nZShyYW5raW5nKSAlPiUgcHVsbChjb3VudHJ5KSAlPiUgYXMuY2hhcmFjdGVyKCkNCnRvcC5jb3VudHJpZXMgJT4lIHNldGRpZmYoJ1dvcmxkJykgJT4lIHByaW50KCkNCg0KYGBgDQoNCmBgYHtyfQ0KZGF0YS5sYXRlc3QgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIoIWlzLm5hKGNvdW50cnkpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9aWZlbHNlKHJhbmtpbmcgPD0gayArIDEsIGFzLmNoYXJhY3Rlcihjb3VudHJ5KSwgJ090aGVycycpKSAlPiUNCiAgbXV0YXRlKGNvdW50cnk9Y291bnRyeSAlPiUgZmFjdG9yKGxldmVscz1jKHRvcC5jb3VudHJpZXMsICdPdGhlcnMnKSkpDQpkYXRhLmxhdGVzdCAlPD4lIGdyb3VwX2J5KGNvdW50cnkpICU+JQ0KICBzdW1tYXJpc2UoY29uZmlybWVkPXN1bShjb25maXJtZWQpLCBuZXcuY29uZmlybWVkPXN1bShuZXcuY29uZmlybWVkKSwNCiAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPXN1bShjdXJyZW50LmNvbmZpcm1lZCksDQogICAgICAgICAgICByZWNvdmVyZWQ9c3VtKHJlY292ZXJlZCksIGRlYXRocz1zdW0oZGVhdGhzKSwgbmV3LmRlYXRocz1zdW0obmV3LmRlYXRocykpICU+JQ0KICBtdXRhdGUoZGVhdGgucmF0ZT0oMTAwICogZGVhdGhzL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKSANCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLCBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQpKSAlPiUNCiAgbXV0YXRlKHJlY292ZXIucmF0ZT0oMTAwICogcmVjb3ZlcmVkL2NvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCmRmX3BvcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBwb3B1bGF0aW9uICIpKQ0KZGZfcG9wIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9wKQ0KZGZfcG9wIDwtIHJlbmFtZShkZl9wb3AsImNvdW50cnkiPSJDb3VudHJ5IikNCmRmX3BvcA0KZGF0YS5sYXRlc3QgPC0gbWVyZ2UoeCA9IGRhdGEubGF0ZXN0LCB5ID0gZGZfcG9wLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSANCmRhdGEubGF0ZXN0IDwtIHJlbmFtZShkYXRhLmxhdGVzdCwicG9wdWxhdGlvbiIgPSAiUG9wdWxhdGlvbiAoMjAyMCkiKQ0KZGF0YS5sYXRlc3QNCmRhdGEubGF0ZXN0ICAlPD4lIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZCxyZWNvdmVyZWQscmVjb3Zlci5yYXRlLHBvcHVsYXRpb24pKSAlPiUNCiAgbXV0YXRlKGNvbmZpcm0ucmF0ZT0oMTAwICpjb25maXJtZWQvcG9wdWxhdGlvbikgJT4lIHJvdW5kKDEpKQ0KZGF0YS5sYXRlc3QNCg0KYGBgDQpgYGB7cn0NCmRhdGEubGF0ZXN0ICU+JSBtdXRhdGUoZGVhdGgucmF0ZT1kZWF0aC5yYXRlICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJyUnKSkNCg0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KZGF0YS5sYXRlc3QubG9uZyA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSB3aXRoIHByb3BlciB0ZXh0IGFuZCBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sYXRlc3QubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nVG90YWwgRGVhdGhzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoLnJhdGU9J0RlYXRoIFJhdGUgKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQ9J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5kZWF0aHM9J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyLnJhdGUgPSAnUmVjb3ZlciBSYXRlKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm0ucmF0ZSA9ICdDb25maXJtZWQgUmF0ZSglKScpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QubG9uZykNCmRhdGEub25lLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nVG90YWwgQ29uZmlybWVkJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdUb3RhbCBEZWF0aHMnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0N1cnJlbnQgQ29uZmlybWVkJykNCmRhdGEudHdvLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nQ29uZmlybWVkIFJhdGUoJSknDQogICAgICAgICAgICAgICAgICAgICAgICN8IHR5cGU9PSdOZXcgQ29uZmlybWVkIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScNCiAgICAgICAgICAgICAgICAgICAgICAgI3wgdHlwZT09J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdEZWF0aCBSYXRlICglKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nUmVjb3ZlciBSYXRlKCUpJykNCmRhdGEudHdvLmRlbQ0KYGBgDQoNCmBgYHtyfQ0KIyMgYmFyIGNoYXJ0DQpkYXRhLm9uZS5kZW0gJT4lIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0yLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTExKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTcpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKSArDQogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2w9MSwgc2NhbGVzPSdmcmVlX3knKQ0KDQpgYGANCmBgYHtyfQ0KZGF0YS50d28uZGVtJGZhY2V0IDwtIGZhY3RvcihkYXRhLnR3by5kZW0kdHlwZSwgbGV2ZWxzID0gYygnQ29uZmlybWVkIFJhdGUoJSknLCAnUmVjb3ZlciBSYXRlKCUpJywnRGVhdGggUmF0ZSAoJSknKSkNCmRhdGEudHdvLmRlbSAlPiUgDQogIGdncGxvdChhZXMoeD1jb3VudHJ5LCB5PWNvdW50LCBmaWxsPWNvdW50cnksIGdyb3VwPWNvdW50cnkpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50LCB5PWNvdW50KSwgc2l6ZT0yLCB2anVzdD0wKSArDQogIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnVG9wIDIwIENvdW50cmllcyB3aXRoIE1vc3QgQ29uZmlybWVkIENhc2VzIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWU9J0NvdW50cnknLCBsYWJlbHM9YWVzKGNvdW50KSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249J25vbmUnLA0KICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTExKSwNCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTkpLA0KICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT02LGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH5mYWNldCwgbmNvbD0xLCBzY2FsZXM9J2ZyZWVfeScpDQpgYGANCg0KYGBge3J9DQojI0dEUA0KZGZfZ2RwIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGdkcCIpKQ0KZGZfZ2RwIDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2RwKQ0KZGZfZ2RwIDwtIHJlbmFtZShkZl9nZHAsImNvdW50cnkiPSJSZWFsIEdEUCBncm93dGggKEFubnVhbCBwZXJjZW50IGNoYW5nZSkiKQ0KZGZfZ2RwIDwtIHNlbGVjdChkZl9nZHAsYygiY291bnRyeSIsIjIwMTIiLCIyMDEzIiwiMjAxNCIsIjIwMTUiLCIyMDE2IiwiMjAxNyIsIjIwMTgiLCIyMDE5IiwiMjAyMCIsIjIwMjEiKSkNCmRmX2dkcA0KZGZfZ2RwMjAxOSA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBnZHAxOSIpKQ0KZGZfZ2RwMjAxOSA8LSBhcy5kYXRhLmZyYW1lKGRmX2dkcDIwMTkpDQpkZl9nZHAyMDE5DQoNCmBgYA0KYGBge3J9DQojaGVhbHRocmFua2luZw0KZGZfaGVhbHQgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gaGVhbHRocmFua2luZyIpKQ0KZGZfaGVhbHQgPC0gYXMuZGF0YS5mcmFtZShkZl9oZWFsdCkNCmRmX2hlYWx0IDwtIHNlbGVjdChkZl9oZWFsdCxjKCJjb3VudHJ5IiwiaGVhbHRoQ2FyZUluZGV4IikpDQpkZl9oZWFsdA0KYGBgDQpgYGB7cn0NCiNUb3AyMFBvcm5odWINCmRmX3Bvcm5odWIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gUG9ybmh1YiIpKQ0KZGZfcG9ybmh1YiA8LSBhcy5kYXRhLmZyYW1lKGRmX3Bvcm5odWIpDQpkZl9wb3JuaHViDQoNCmBgYA0KDQoNCmBgYHtyfQ0KI3RlbXANCmRmX3RlbXAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gQXZnX1dvcmxkX1RlbXBfMjAyMCIpKQ0KZGZfdGVtcCA8LSBhcy5kYXRhLmZyYW1lKGRmX3RlbXApDQpkZl9jaXR5IDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJDaXR5IikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KSAlPiUgDQogIHJlbmFtZShjaXR5PUNpdHkpDQpudW1vZmNpdHkgPC0gYWdncmVnYXRlKGNpdHkgfiBjb3VudHJ5LCBkYXRhID0gZGZfY2l0eSwgbGVuZ3RoKQ0KZGZfdGVtcCA8LSBzZWxlY3QoZGZfdGVtcCxjKCJDb3VudHJ5IiwiQXByIiwiTWF5IiwiSnVuIiwiSnVsIiwiQXVnIikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KQ0KZGZfdGVtcCA8LSBkYXRhLmZyYW1lKGNvdW50cnk9ZGZfdGVtcFssMV0sYXZnPXJvd01lYW5zKGRmX3RlbXBbLC0xXSkpDQpkZl90ZW1wIDwtIGRmX3RlbXAgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUgc3VtbWFyaXNlKGF2Z190ZW1wID0gbWVhbihhdmcsbmEucm0gPSBUUlVFKSkNCmRmX3RlbXAgPC0gZGZfdGVtcCAlPiUgbXV0YXRlKGNvdW50cnk9aWZlbHNlKGNvdW50cnk9PSJVbml0ZWQgU3RhdGVzIiwiVVMiLCBjb3VudHJ5ICkgKSANCmRmX3RlbXAkYXZnX3RlbXAgPC0gZGZfdGVtcCRhdmdfdGVtcCAlPiUgDQogIHNwcmludGYoZGZfdGVtcCRhdmdfdGVtcCwgZm10ID0gJyUjLjFmJykgJT4lDQogIGFzLm51bWVyaWMoZGZfdGVtcCRhdmdfdGVtcCkNCmRmX3RlbXANCmBgYA0KDQoNCg0KYGBge3J9DQojVG9wIDIwIHdpdGggZ2RwDQpkYXRhLmxvbmdHRFAgPC0gZGZfZ2RwICU+JSBnYXRoZXIoa2V5PXllYXIsIHZhbHVlPUdEUCwgLWMoY291bnRyeSkpDQpkYXRhLnRvcCA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcpDQpkYXRhLnRvcCA8LSBoZWFkKGRhdGEudG9wLDIwKQ0KI1ZpZXcoZGF0YS50b3ApDQpkYXRhLmdkcCA8LSBmaWx0ZXIoZGF0YS5sb25nR0RQLHllYXI9PScyMDIwJykNCiNWaWV3KGRhdGEuZ2RwKQ0KI21lcmdlDQptZXJnY291bnRyeSA9IGZ1bmN0aW9uKGRhdGExLGRhdGEyKXsNCiAgZGF0YSA8LSBtZXJnZSh4ID0gZGF0YTEsIHkgPSBkYXRhMiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQogIHJldHVybihkYXRhKQ0KfQ0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLCB5ID0gZGZfZ2RwMjAxOSwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lIA0KICBzZWxlY3QoLWMoY29kZSxyYW5rLG5ldy5jb25maXJtZWQsbmV3LmRlYXRocyxjdXJyZW50LmNvbmZpcm1lZCxwb3B1bGF0aW9uKSkgJT4lIA0KICByZW5hbWUoR0RQPSJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfaGVhbHQsIGJ5ID0gImNvdW50cnkiLCBhbGwueCA9IFRSVUUpICU+JQ0KICByZW5hbWUoaGVhbHRoY2FyZT0iaGVhbHRoQ2FyZUluZGV4IikNCiNkYXRhLnRvcC53b3JsZCA8LSBtZXJnY291bnRyeShkYXRhLnRvcC53b3JsZCwgZGZfdGVtcCkNCg0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLndvcmxkLCB5ID0gZGZfcG9ybmh1YiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lDQogIHJlbmFtZShQb3JuaHViID0gIlBvcm5odWJJbmRleCglKSIpDQoNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdjb3VudHJ5KGRhdGEudG9wLndvcmxkLCBkZl90ZW1wKQ0KaW5kZXggPC0gaXMubmEoZGF0YS50b3Aud29ybGQpDQpkYXRhLnRvcC53b3JsZFtpbmRleF0gPC0gMA0KZGF0YS50b3Aud29ybGQNCiNWaWV3KGRhdGEudG9wLndvcmxkKQ0KDQpub3JtYWxpemUgPSBmdW5jdGlvbihkYXRhKXsNCiAgI3JldHVybiAoKGRhdGEgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKS8obWF4KGRhdGEsbmEucm0gPSBUUlVFKSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpKQ0KICB6IDwtIHNjYWxlKGRhdGEpOw0KICB0YW5oKHovMikNCn0NCm5vcm1fZGF0YSA9IGFzLmRhdGEuZnJhbWUoYXBwbHkoZGF0YS50b3Aud29ybGRbLDI6MTJdLDIsbm9ybWFsaXplKSkNCmNvcnJfZGF0YSA8LSBub3JtX2RhdGENCm5vcm1fZGF0YSRjb3VudHJ5IDwtIGMoIkFyZ2VudGluYSIsIkJhbmdsYWRlc2giLCJCcmF6aWwiLCJDaGlsZSIsIkNvbG9tYmlhIiwiRnJhbmNlIiwiR2VybWFueSIsIkluZGlhIiwiSXJhbiIsIkl0YWx5IiwiTWV4aWNvIiwiUGFraXN0YW4iLCJQZXJ1IiwiUnVzc2lhIiwic2F1ZGkgQXJhYmlhIiwiU291dGggQWZyaWNhIiwiU3BhaW4iLCJUdXJrZXkiLCJVbml0ZWQgS2luZ2RvbSIsIlVTIikNCiNWaWV3KG5vcm1fZGF0YSkNCg0KDQpub3JtX2RhdGFfcGxvdCA8LSBzZWxlY3Qobm9ybV9kYXRhLCJjb3VudHJ5IiwiY29uZmlybS5yYXRlIiwiZGVhdGgucmF0ZSIsInJlY292ZXIucmF0ZSIsImhlYWx0aGNhcmUiLCJQb3JuaHViIiwiR0RQIiwiYXZnX3RlbXAiKQ0Kbm9ybV9kYXRhX3Bsb3QgJTw+JSBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhjb3VudHJ5KSkNCmxldmVsX29yZGVyIDwtIGZhY3Rvcihub3JtX2RhdGFfcGxvdCR0eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbCA9IGMoIkdEUCIsImF2Z190ZW1wIiwiaGVhbHRoY2FyZSIsInJlY292ZXIucmF0ZSIsImRlYXRoLnJhdGUiLCJjb25maXJtLnJhdGUiLCJQb3JuaHViIikpDQpnZ3Bsb3QoZGF0YSA9IG5vcm1fZGF0YV9wbG90LCBhZXMoeD1jb3VudHJ5LCB5PWxldmVsX29yZGVyLCBmaWxsPWNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJwaW5rIiwgaGlnaCA9ICJibHVlIikgKw0KICB4bGFiKCIiKSArDQogIHlsYWIoIiIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsdmp1c3QgPSAxKSkrDQogIHRoZW1lKA0KICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICkNCg0KICANCmBgYA0KDQoNCmBgYHtyfQ0KI3JhbmsgR0RQDQpkYXRhLnRvcC5oaWdodCA8LSBkYXRhLmdkcCAlPiUgc2VsZWN0KGNvdW50cnksIHllYXIsR0RQKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKGRlc2MoR0RQKSkpDQpkYXRhLnRvcC5oaWdodA0KayA8LSAxNQ0KdG9wLmdkcCA8LSBkYXRhLnRvcC5oaWdodCAlPiUgDQogICNmaWx0ZXIocmFua2luZyA8PSBrICsgMSkgJT4lIA0KICBhcnJhbmdlKHJhbmtpbmcpDQp0b3AuZ2RwIDwtIGhlYWQodG9wLmdkcCwyMSkNCmRhdGEudG9wLmxvdyA8LSBkYXRhLmdkcCAlPiUgc2VsZWN0KGNvdW50cnksIHllYXIsR0RQKSAlPiUNCiAgbXV0YXRlKHJhbmtpbmcgPSBkZW5zZV9yYW5rKEdEUCkpDQpsb3cuZ2RwLmxvbmcgPC0gZGF0YS50b3AubG93ICU+JSANCiAgI2ZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUgDQogIGFycmFuZ2UocmFua2luZykNClZpZXcobG93LmdkcC5sb25nKQ0KbG93LmdkcCA8LSBoZWFkKGxvdy5nZHAubG9uZywyMykNCmxvdy5nZHANCmBgYA0KDQoNCg0KYGBge3J9DQojY29ycmVsYXRpb24NCmNvcnJfZGF0YSAlPD4lIHNlbGVjdChjKEdEUCxjb25maXJtLnJhdGUsZGVhdGgucmF0ZSxyZWNvdmVyLnJhdGUsaGVhbHRoY2FyZSxhdmdfdGVtcCxQb3JuaHViKSkNCmhlYWQoY29ycl9kYXRhKQ0KY29yKGNvcnJfZGF0YSkNCmdnY29ycnBsb3QoY29yKGNvcnJfZGF0YSksaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBvdXRsaW5lLmNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzZEOUVDMSIsIndoaXRlIiwiI0U0NjcyNiIpLA0KICAgICAgICAgICBsYWIgPSBUUlVFKQ0KYGBgDQpgYGB7cn0NCmRmIDwtIGRhdGEubG9uZyAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSB0b3AuY291bnRyaWVzKSAlPD4lDQogIG11dGF0ZShjb3VudHJ5PWNvdW50cnkgJT4lIGZhY3RvcihsZXZlbHM9Yyh0b3AuY291bnRyaWVzKSkpDQpkZiAlPiUgZmlsdGVyKGNvdW50cnkgIT0gJ1dvcmxkJyAmIHR5cGUgIT0gJ1RvdGFsIENvbmZpcm1lZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCwgZmlsbD10eXBlKSkgKw0KICBnZW9tX2FyZWEoYWxwaGE9MC41KSArDQojIHhsYWIoJycpICsgeWxhYignJykgKw0KICBsYWJzKHRpdGxlPXBhc3RlMCgnTnVtYmVycyBvZiBDT1ZJRC0xOSBDYXNlcyBpbiBUb3AgMjAgQ291bnRyaWVzIC0gJywNCiAgICAgICAgICAgICAgICAgICAgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjQsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIHN0cmlwLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgZmFjZXRfd3JhcCh+Y291bnRyeSwgbmNvbD00LCBzY2FsZXM9J2ZyZWVfeScpDQoNCmBgYA0KYGBge3J9DQpwIDwtIGRmICU+JSBmaWx0ZXIoY291bnRyeSAhPSAnV29ybGQnKSAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQsIGNvbG9yPXR5cGUpKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ09WSUQtMTkgQ2FzZXMgaW4gVG9wIDIwIENvdW50cmllcyAobG9nIHNjYWxlKSAtICcsDQogICAgICAgICAgICAgICAgICAgIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCdwdXJwbGUnLCAncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTApLA0KICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQua2V5LnNpemU9dW5pdCgwLjQsICdjbScpLA0KICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIHN0cmlwLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQpwICsgZmFjZXRfd3JhcCh+Y291bnRyeSwgbmNvbD00LCBzY2FsZXM9J2ZyZWVfeScpDQpgYGANCmBgYHtyfQ0KZGF0YS53b3JsZCAlPD4lIGFycmFuZ2UoZGVzYyhkYXRlKSkgJT4lDQogIHNlbGVjdChjKGRhdGUsIGNvbmZpcm1lZCwgZGVhdGhzLCByZWNvdmVyZWQsIGN1cnJlbnQuY29uZmlybWVkLG5ldy5jb25maXJtZWQsIG5ldy5kZWF0aHMsIG5ldy5yZWNvdmVyZWQsIHJhdGUubG93ZXIsIHJhdGUudXBwZXIsIHJhdGUuZGFpbHkpKQ0KZGF0YS53b3JsZCAlPiUNCiAgbXV0YXRlKHJhdGUudXBwZXIgPSByYXRlLnVwcGVyICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJ1xcJScpLA0KICAgICAgICAgcmF0ZS5sb3dlciA9IHJhdGUubG93ZXIgJT4lIGZvcm1hdChuc21hbGw9MSkgJT4lIHBhc3RlMCgnXFwlJyksDQogICAgICAgICByYXRlLmRhaWx5ID0gcmF0ZS5kYWlseSAlPiUgZm9ybWF0KG5zbWFsbD0xKSAlPiUgcGFzdGUwKCdcXCUnKSkgDQpgYGANCmBgYHtyfQ0KI3NhcnNfMjAwMw0KZGZfc2FycyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBzYXJzXzIwMDNfdXBkYXRlIikpDQpkZl9zYXJzIDwtIGFzLmRhdGEuZnJhbWUoZGZfc2FycykNCmRmX3NhcnMNCmBgYA0KDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIGNoYXJhY3RlciB0byBkYXRlDQojZGF0ZXNTYXIgPC0gYXMuRGF0ZShkZl9zYXJzJERhdGUsZm9ybWF0ID0gIiVtLyVkLyV5IikNCg0KZGZfc2FycyAlPD4lICBtdXRhdGUoRGF0ZSA9IGFzLkRhdGUoZGZfc2FycyREYXRlLGZvcm1hdCA9ICIlbS8lZC8leSIpKQ0KZGZfc2Fycw0KYGBgDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGFTYXIubG9uZyA8LSBkZl9zYXJzICU+JQ0KICBzZWxlY3QoYyhEYXRlLCBjb3VudHJ5LCBDdW11bGF0aXZlX251bWJlciAsIE51bWJlcl9kZWF0aHMsIE51bWJlcl9yZWNvdmVyZWQpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgRGF0ZSkpDQojIyBzZXQgZmFjdG9yIGxldmVscyB0byBzaG93IHRoZW0gaW4gYSBkZXNpcmFibGUgb3JkZXINCmRhdGFTYXIubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwgQ3VtdWxhdGl2ZV9udW1iZXIgPSdDdW11bGF0aXZlIE51bWJlcicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE51bWJlcl9kZWF0aHMgPSdOdW1iZXIgb2YgZGVhdGhzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTnVtYmVyX3JlY292ZXJlZCA9J051bWJlciBvZiByZWNvdmVyZWQnKSkNClZpZXcoZGF0YVNhci5sb25nKQ0KIA0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KZyA8LQ0KICBnZ3Bsb3QoZGF0YVNhci5sb25nLGFlcyhEYXRlLGNvdW50LGNvbG9yID0gdHlwZSkpICsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fcG9pbnQoKSsNCiAgeGxhYigiIikrDQogIHlsYWIoIiIpDQpnDQogIA0KYGBgDQpgYGB7cn0NCiNDb3ZpZF9UaGFpbGFuZA0KZGZfdGhhaSA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF9UaGFpbGFuZCIpKQ0KZGZfdGhhaSA8LSBhcy5kYXRhLmZyYW1lKGRmX3RoYWkpDQpWaWV3KGRmX3RoYWkpDQpgYGANCg0KYGBge3J9DQojY2xlYW4gQ292aWRfVGhhaWxhbmQNCmRhdGVzLnRoIDwtIGRmX3RoYWlbLDJdJT4lIG1keSgpDQpyYW5nZShkYXRlcy50aCkNCm1pbi5kYXRlLnRoIDwtIG1pbihkYXRlcy50aCkNCm1heC5kYXRlLnRoIDwtIG1heChkYXRlcy50aCkNCm1pbi5kYXRlLnR4dC50aCA8LSBtaW4uZGF0ZS50aCAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQudGggPC0gbWF4LmRhdGUudGggJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KYGBgDQpgYGB7cn0NCmRmX3RoYWkkYW5ub3VuY2VfZGF0ZSA8LSBtZHkoZGZfdGhhaSRhbm5vdW5jZV9kYXRlKQ0KZGZfdGhhaSRub3RpZmljYXRpb25fZGF0ZSA8LSBtZHkoZGZfdGhhaSRub3RpZmljYXRpb25fZGF0ZSkNCmRmX3RoYWkgDQpgYGANCmBgYHtyfQ0KZGZfdGhhaSA8LSBkZl90aGFpICU+JSBzZWxlY3QoIU5vLikgJT4lIHNlbGVjdCghbm90aWZpY2F0aW9uX2RhdGUpICU+JSANCiAgZ3JvdXBfYnkoYW5ub3VuY2VfZGF0ZSkNCmRmX3RoYWkNCmBgYA0KYGBge3J9DQojIFRvdGFsIGNvbmZpcm1lZCBjYXNlcyBpbiBUaGFpbGFuZA0KZGF0YS50aGFpLmNvdW50IDwtIGRmX3RoYWkgJT4lDQogIHNlbGVjdChhbm5vdW5jZV9kYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvbWZpcm1lZCA9IG4oKSkgICU+JSBhcy5kYXRhLmZyYW1lKCkNCmRhdGEudGhhaS5jb3VudCRjdW11bGF0aXZlX2NvbmZpcm1lZCA8LSBjdW1zdW0oZGF0YS50aGFpLmNvdW50WywgMl0pDQpkYXRhLnRoYWkuY291bnQNCmBgYA0KDQoNCmBgYHtyfQ0KIyMgVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjENCnBsb3QxIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y3VtdWxhdGl2ZV9jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCIgIikgKyB5bGFiKCJDb3VudCIpICsgbGFicyh0aXRsZT0nVGhhaSBDdW11bGF0aXZlIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSknKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSBnZ3Bsb3QoZGF0YS50aGFpLmNvdW50LCBhZXMoeD1hbm5vdW5jZV9kYXRlLCB5PWNvbWZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoIiAiKSArIHlsYWIoIkNvdW50IikrIGxhYnModGl0bGU9J1RoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKScpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0xKQ0KYGBgDQpgYGB7cn0NCiMjIFRoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKSBsb2cgc2NhbGUNCnBsb3QxIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y3VtdWxhdGl2ZV9jb25maXJtZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCIgIikgKyB5bGFiKCJDb3VudCIpICsgbGFicyh0aXRsZT0nVGhhaSBDdW11bGF0aXZlIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBKYW4gMjAyMSBsb2cgc2NhbGUpJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKStzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCnBsb3QyIDwtIGdncGxvdChkYXRhLnRoYWkuY291bnQsIGFlcyh4PWFubm91bmNlX2RhdGUsIHk9Y29tZmlybWVkKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYigiICIpICsgeWxhYigiQ291bnQiKSsgbGFicyh0aXRsZT0nVGhhaSBDb25maXJtZWQgQ2FzZXMgKEphbiAyMDIwIC0gSmFuIDIwMjEgbG9nIHNjYWxlKScpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkrc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MSkNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgc2V4IChnZW5kZXIpDQpkYXRhLnRoYWkuZ2VuZGVyIDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KHNleCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+MSklPiUNCiAgI211dGF0ZShwb3MgPSBjdW1zdW0ocGVyY2VudCkgLSAwLjUqcGVyY2VudCkgJT4lDQogIGFycmFuZ2UoZGVzYyhwZXJjZW50KSkNCmRhdGEudGhhaS5nZW5kZXINCmBgYA0KYGBge3J9DQpkYXRhLnRoYWkuZ2VuZGVyJHNleCA8LSBmYWN0b3IoZGF0YS50aGFpLmdlbmRlciRzZXgsIGxldmVscyA9IGFzLmNoYXJhY3RlcihkYXRhLnRoYWkuZ2VuZGVyJHNleCkpDQpkYXRhLnRoYWkuZ2VuZGVyJHNleA0KZy50aC5nZW5kZXIgPC0gZGF0YS50aGFpLmdlbmRlciAlPiUgDQogIGdncGxvdChhZXMoeCA9ICIiLCB5ID0gcGVyY2VudCwgZmlsbCA9IHNleCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgdGhlbWVfdm9pZCgpICsNCiAgbGFicyh0aXRsZT0nR2VuZGVyIG9mIFRoYWkgQ29uZmlybWVkIENhc2VzIChKYW4gMjAyMCAtIEphbiAyMDIxKScpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHBlcmNlbnQsICIlIikpLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSA1LCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVFJVRSkpIA0KZy50aC5nZW5kZXINCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgcmlzaw0KZGF0YS50aGFpLnJpc2sgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkocmlzaykgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQpkYXRhLnRoYWkucmlzaw0KYGBgDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgcmlzaw0KZGF0YS50aGFpLnJpc2sgPC0gZGZfdGhhaSAlPiUNCiAgZ3JvdXBfYnkocmlzaykgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIG11dGF0ZShwZXJjZW50ID0gKGNvdW50IC8gc3VtKGNvdW50KSAqIDEwMCkgJT4lIHJvdW5kKDIpKSAlPiUNCiAgZmlsdGVyKHBlcmNlbnQ+My41KSAlPiUNCiAgYXJyYW5nZShkZXNjKHBlcmNlbnQpKSANCmRhdGEudGhhaS5yaXNrDQpgYGANCmBgYHtyfQ0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJDIl0gPC0gIkNsb3NlIGNvbnRhY3Qgd2l0aCB0aGUgcGF0aWVudCINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiRiJdIDwtICJTdGF0ZSBRdWFyYW50aW5lIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJPIl0gPC0gIkFPUS9BTFEvSFEvQUhRL09RIg0KZGF0YS50aGFpLnJpc2skcmlza1tkYXRhLnRoYWkucmlzayRyaXNrID09ICJHIl0gPC0gIkdvIHRvIGEgY3Jvd2RlZCBwbGFjZSINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiQiJdIDwtICJUaGFpIHBlb3BsZSBmcm9tIGFicm9hZCINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiRCJdIDwtICJDYXJlZXIgYXQgcmlzayINCmRhdGEudGhhaS5yaXNrJHJpc2tbZGF0YS50aGFpLnJpc2skcmlzayA9PSAiSCJdIDwtICJDYWJhcmV0Ig0KZGF0YS50aGFpLnJpc2sNCmBgYA0KDQoNCmBgYHtyfQ0KZGF0YS50aGFpLnJpc2skcmlzayA8LSBmYWN0b3IoZGF0YS50aGFpLnJpc2skcmlzaywgbGV2ZWxzID0gYXMuY2hhcmFjdGVyKGRhdGEudGhhaS5yaXNrJHJpc2spKQ0KZGF0YS50aGFpLnJpc2skcmlzaw0KZy50aC5yaXNrIDwtIGRhdGEudGhhaS5yaXNrICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50LCBmaWxsID0gcmlzaykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC41KSArDQogIGNvb3JkX3BvbGFyKCJ5IikgKw0KICB0aGVtZV92b2lkKCkgKw0KICBsYWJzKHRpdGxlPSdSaXNrIG9mIFRoYWkgQ29uZmlybWVkIENhc2VzKEphbiAyMDIwIC0gSmFuIDIwMjEpJykrDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZTAocGVyY2VudCwgIiUiKSksIGNvbG9yID0gIkJsYWNrIiwgc2l6ZSA9IDMsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHJldmVyc2UgPSBUUlVFKSkgDQpnLnRoLnJpc2sNCmBgYA0KDQoNCg0KYGBge3J9DQojIENvbmZpcm1lZCBjYXNlcyBkaXZpZGVkIGJ5IGFnZQ0KZGF0YS50aGFpLmFnZSA8LSBkZl90aGFpICU+JQ0KICBncm91cF9ieShhZ2Usc2V4KSAlPiUgDQogIGZpbHRlcihzZXggIT0gIiIpJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQpkYXRhLnRoYWkuYWdlDQpgYGANCmBgYHtyfQ0KZ2dwbG90KGRhdGEudGhhaS5hZ2UsYWVzKHg9YWdlLHk9Y291bnQsZmlsbD1zZXgpKStnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrDQogIGxhYnModGl0bGU9J0FnZSBvZiBUaGFpIENvbmZpcm1lZCBDYXNlcycpK2d1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChyZXZlcnNlID0gVCkpDQoNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWVkIGNhc2VzIGRpdmlkZWQgYnkgbmF0aW9uYWxpdHkNCg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5IDwtIGRmX3RoYWkgJT4lDQogIGdyb3VwX2J5KG5hdGlvbmFsaXR5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgZmlsdGVyKGNvdW50ID4gMTEpJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpDQpkYXRhLnRoYWkubmF0aW9uYWxpdHkkbmF0aW9uYWxpdHlbZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5ID09ICI/Pz8/Pz8/PyJdIDwtICJVbmtub3duIg0KZGF0YS50aGFpLm5hdGlvbmFsaXR5JG5hdGlvbmFsaXR5W2RhdGEudGhhaS5uYXRpb25hbGl0eSRuYXRpb25hbGl0eSA9PSAiIl0gPC0gIlVua25vd24iDQpkYXRhLnRoYWkubmF0aW9uYWxpdHkNCmdncGxvdChkYXRhLnRoYWkubmF0aW9uYWxpdHksYWVzKHg9bmF0aW9uYWxpdHkseT1jb3VudCkpK2dlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSsNCiAgbGFicyh0aXRsZT0nTmF0aW9uYWxpdHkgb2YgVGhhaSBDb25maXJtZWQgQ2FzZXMnKStjb29yZF9mbGlwKCkNCmBgYA0KYGBge3J9DQojQ292aWRfVVMNCmRmX3VzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGNvdmlkVXMiKSkNCmRmX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfdXMpDQpkZl91cw0KYGBgDQpgYGB7cn0NCiNjbGVhbiBDb3ZpZF9VUw0KZGF0ZXMudXMgPC0gZGZfdXNbLDJdJT4lIG1keSgpDQpyYW5nZShkYXRlcy51cykNCm1pbi5kYXRlLnVzIDwtIG1pbihkYXRlcy51cykNCm1heC5kYXRlLnVzIDwtIG1heChkYXRlcy51cykNCm1pbi5kYXRlLnR4dC51cyA8LSBtaW4uZGF0ZS51cyAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQptYXguZGF0ZS50eHQudXMgPC0gbWF4LmRhdGUudXMgJT4lIGZvcm1hdCgnJWQgJWIgJVknKQ0KYGBgDQoNCmBgYHtyfQ0KZGZfdXMkZGF0ZSA8LSBtZHkoZGZfdXMkZGF0ZSkNCmRmX3VzDQpgYGANCg0KDQpgYGB7cn0NCmRmX3VzIDwtIGRmX3VzICU+JSBzZWxlY3QoIU15VW5rbm93bkNvbHVtbikgJT4lIHNlbGVjdCghZmlwcykgJT4lIA0KICBncm91cF9ieShkYXRlKQ0KZGZfdXMNCmBgYA0KDQoNCmBgYHtyfQ0KIyMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQNCmRhdGEubG9uZy51cyA8LSBkZl91cyAlPiUNCiAgc2VsZWN0KGMoZGF0ZSwgc3RhdGUsIGNhc2VzLCBkZWF0aHMpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoZGF0ZSwgc3RhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxvbmcudXMgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIGNhc2VzPSdDb25maXJtZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J0RlYXRocycpKQ0KDQpWaWV3KGRhdGEubG9uZy51cykNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEubG9uZy51cyxhZXMoeD1kYXRlLHk9Y291bnQpKSsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvcj10eXBlKSkrIGxhYnModGl0bGU9J1VTIENvbmZpcm1lZCBDYXNlcyAoSmFuIDIwMjAgLSBEZWMgMjAyMCknKQ0KDQpgYGANCmBgYHtyfQ0KI2dlbmRlciBpbiB1cw0KZGZfZ2VuZGVyX3VzIDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGRhdGFfZ2VuZGVyIikpDQpkZl9nZW5kZXJfdXMgPC0gYXMuZGF0YS5mcmFtZShkZl9nZW5kZXJfdXMpDQpkZl9nZW5kZXJfdXMgPC0gc2VsZWN0KGRmX2dlbmRlcl91cyxjKCJTdGF0ZSIsIk1hbGUiLCJGZW1hbGUiKSkNCmRmX2dlbmRlcl91cyA8LSByZW5hbWUoZGZfZ2VuZGVyX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX2dlbmRlcl91cw0KYGBgDQpgYGB7cn0NCiNwb3B1bGF0aW9uIGluIHVzDQpkZl9wb3BfdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9wb3B1bGF0aW9uIikpDQpkZl9wb3BfdXMgPC0gYXMuZGF0YS5mcmFtZShkZl9wb3BfdXMpDQpkZl9wb3BfdXMgPC0gc2VsZWN0KGRmX3BvcF91cyxjKCJTdGF0ZSIsIlBvcHVsYXRpb24iKSkNCmRmX3BvcF91cyA8LSByZW5hbWUoZGZfcG9wX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX3BvcF91cw0KYGBgDQpgYGB7cn0NCiNsb2NrZG93biBpbiB1cw0KZGZfbG9ja2Rvd25fdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZGF0YV9sb2NrZG93biIpKQ0KZGZfbG9ja2Rvd25fdXMgPC0gYXMuZGF0YS5mcmFtZShkZl9sb2NrZG93bl91cykNCmRmX2xvY2tkb3duX3VzIDwtIHNlbGVjdChkZl9sb2NrZG93bl91cyxjKCJTdGF0ZSIsIkRheSBsb2NrZG93biIpKQ0KZGZfbG9ja2Rvd25fdXMgPC0gcmVuYW1lKGRmX2xvY2tkb3duX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX2xvY2tkb3duX3VzDQpgYGANCg0KYGBge3J9DQojR0RQIGluIHVzDQpkZl9nZHBfdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwX3VzIikpDQpkZl9nZHBfdXMgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHBfdXMpDQpkZl9nZHBfdXMgPC0gc2VsZWN0KGRmX2dkcF91cyxjKCJTdGF0ZSIsIkdEUHMiKSkNCmRmX2dkcF91cyA8LSByZW5hbWUoZGZfZ2RwX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX2dkcF91cw0KYGBgDQpgYGB7cn0NCiNob21lbGVzcyBpbiB1cw0KZGZfaG9tZWxlc3NfdXMgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdXNfaG9tZWxlc3MiKSkNCmRmX2hvbWVsZXNzX3VzIDwtIGFzLmRhdGEuZnJhbWUoZGZfaG9tZWxlc3NfdXMpDQpkZl9ob21lbGVzc191cyA8LSBzZWxlY3QoZGZfaG9tZWxlc3NfdXMsYygiU3RhdGUiLCJIb21lbGVzcyIpKQ0KZGZfaG9tZWxlc3NfdXMgPC0gcmVuYW1lKGRmX2hvbWVsZXNzX3VzLCJzdGF0ZSI9IlN0YXRlIikNCmRmX2hvbWVsZXNzX3VzDQpgYGANCg0KDQpgYGB7cn0NCiNtZXJnZQ0KbWVyZ2NvdW50cnkgPSBmdW5jdGlvbihkYXRhMSxkYXRhMil7DQogIGRhdGEgPC0gbWVyZ2UoeCA9IGRhdGExLCB5ID0gZGF0YTIsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUUlVFKSANCiAgcmV0dXJuKGRhdGEpDQp9DQoNCmRmX0FsbHVzIDwtIG1lcmdlKHggPSBkZl91cywgeSA9IGRmX2dlbmRlcl91cywgYnkgPSAic3RhdGUiLCBhbGwueCA9IFRSVUUpIA0KDQpkZl9BbGx1cyA8LSBtZXJnZSh4ID0gZGZfQWxsdXMsIHkgPSBkZl9wb3BfdXMsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUUlVFKSANCg0KZGZfQWxsdXMgPC0gbWVyZ2UoeCA9IGRmX0FsbHVzLCB5ID0gZGZfbG9ja2Rvd25fdXMsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUUlVFKSANCg0KZGZfQWxsdXMgPC0gbWVyZ2UoeCA9IGRmX0FsbHVzLCB5ID0gZGZfZ2RwX3VzLCBieSA9ICJzdGF0ZSIsIGFsbC54ID0gVFJVRSkNCg0KZGZfQWxsdXMgPC0gbWVyZ2UoeCA9IGRmX0FsbHVzLCB5ID0gZGZfaG9tZWxlc3NfdXMsIGJ5ID0gInN0YXRlIiwgYWxsLnggPSBUUlVFKQ0KDQpWaWV3KGRmX0FsbHVzKQ0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQppbmRleCA8LSBpcy5uYShkZl9BbGx1cykNCmRmX0FsbHVzW2luZGV4XSA8LSAwDQoNCm5vcm1hbGl6ZSA9IGZ1bmN0aW9uKGRhdGEpew0KICAjcmV0dXJuICgoZGF0YSAtIG1pbihkYXRhLG5hLnJtID0gVFJVRSkpLyhtYXgoZGF0YSxuYS5ybSA9IFRSVUUpIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkpDQogIHogPC0gc2NhbGUoZGF0YSk7DQogIHRhbmgoei8yKQ0KfQ0KQWxsdXMgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRmX0FsbHVzWywzOjEwXSwyLG5vcm1hbGl6ZSkpDQpjb3JyX2RhdGFVUyA8LSBBbGx1cyANCg0KVmlldyhBbGx1cykNCg0KYGBgDQoNCmBgYHtyfQ0KQWxsdXMkc3RhdGUgPC0gYyhkZl9BbGx1cyRzdGF0ZSkNClZpZXcoQWxsdXMpDQpgYGANCg0KDQpgYGB7cn0NCg0KDQpBbGx1c19wbG90IDwtIHNlbGVjdChBbGx1cywic3RhdGUiLCJjYXNlcyIsImRlYXRocyIsIk1hbGUiLCJGZW1hbGUiLCJQb3B1bGF0aW9uIiwiRGF5IGxvY2tkb3duIiwiR0RQcyIsIkhvbWVsZXNzIikNCkFsbHVzX3Bsb3QgJTw+JSBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtYyhzdGF0ZSkpDQpsZXZlbF9vcmRlciA8LSBmYWN0b3IoQWxsdXNfcGxvdCR0eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbCA9IGMoImNhc2VzIiwiZGVhdGhzIiwiTWFsZSIsIkZlbWFsZSIsIlBvcHVsYXRpb24iLCJEYXkgbG9ja2Rvd24iLCJHRFBzIiwiSG9tZWxlc3MiKSkNCmdncGxvdChkYXRhID0gQWxsdXNfcGxvdCwgYWVzKHg9c3RhdGUsIHk9bGV2ZWxfb3JkZXIsIGZpbGw9Y291bnQpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gInBpbmsiLCBoaWdoID0gImJsdWUiKSArDQogIHhsYWIoIiIpICsNCiAgeWxhYigiIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCx2anVzdCA9IDEpKSsNCiAgdGhlbWUoDQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICNsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQoNCg0KYGBgDQpgYGB7cn0NCmNvcnJfZGF0YVVTIDwtIHJlbmFtZShjb3JyX2RhdGFVUywiRGF5bG9ja2Rvd24iPSJEYXkgbG9ja2Rvd24iKQ0KYGBgDQoNCg0KYGBge3J9DQojY29ycmVsYXRpb24NCmNvcnJfZGF0YVVTICU8PiUgc2VsZWN0KGMoY2FzZXMsZGVhdGhzLE1hbGUsRmVtYWxlLFBvcHVsYXRpb24sRGF5bG9ja2Rvd24sR0RQcyxIb21lbGVzcykpDQpoZWFkKGNvcnJfZGF0YVVTKQ0KY29yKGNvcnJfZGF0YVVTKQ0KZ2djb3JycGxvdChjb3IoY29ycl9kYXRhVVMpLGhjLm9yZGVyID0gVFJVRSwNCiAgICAgICAgICAgb3V0bGluZS5jb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgIGNvbG9ycyA9IGMoIiM2RDlFQzEiLCJ3aGl0ZSIsIiNFNDY3MjYiKSwNCiAgICAgICAgICAgbGFiID0gVFJVRSkNCg0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K